

#ifndef STATICMATRIX_H
#define STATICMATRIX_H

#include <cassert>
#include <functional>
#include <iostream>

template<class T,unsigned int _row,unsigned int _col,class _Equal=std::equal_to<T> > 
class StaticMatrix
{
	enum { _Row = _row, _Col = _col};

	//friend class StaticMatrix<T,_col,_row,_Equal>;
	//template<unsigned int ColAndRow> friend StaticMatrix<T,_Row,_Col> operator * (const StaticMatrix<T,_Row,ColAndRow>& _left,const StaticMatrix<T,ColAndRow,_Col>& _right);
	//template<unsigned int Row>friend StaticMatrix<T,Row,_Col> operator * (const StaticMatrix<T,Row,_Row>& _left,const StaticMatrix<T,_Row,_Col>& _right);
	//template<unsigned int Col>friend StaticMatrix<T,_Row,Col> operator * (const StaticMatrix<T,_Row,_Col>& _left,const StaticMatrix<T,_Col,Col>& _right);
public:

	StaticMatrix(_Equal _equaler=_Equal())
		:m_Equaler(_equaler)
	{
		for(size_t i=0;i<_Row;++i)
		for(size_t j=0;j<_Col;++j)
		{
			m_Data[i][j] = T();
		}
	}

	inline size_t RowNo () const { return _Row; }
	inline size_t ColNo () const { return _Col; }

	// Subscript operator
	inline T&		operator () (size_t row, size_t col) 
	{
		assert( row < _Row && col < _Col );
		return m_Data[row][col];
	}
	inline const T& operator () (size_t row, size_t col) const
	{
		assert( row < _Row && col < _Col );
		return m_Data[row][col];
	}
	inline const T& At(size_t row, size_t col)const
	{
		assert( row < _Row && col < _Col );
		return m_Data[row][col];
	}

	bool operator==(const StaticMatrix& matrix)const
	{
		for(size_t row = 0;row < _Row;++row)
		for(size_t col = 0;col < _Col;++col)
		{
			if ( !m_Equaler(m_Data[row][col],matrix.m_Data[row][col]) ) 
				return false;
		}
		return true;
	}

	bool operator!=(const StaticMatrix& matrix)const
	{
		for(size_t row = 0;row < _Row;++row)
		for(size_t col = 0;col < _Col;++col)
		{
			if ( m_Equaler(m_Data[row][col],matrix.m_Data[row][col]) ) 
				return true;
		}
		return false;
	}

	inline StaticMatrix operator + ()const { return *this;}
	inline StaticMatrix operator - ()const { return *this *= -1}

	inline StaticMatrix operator + (const StaticMatrix& m)const
	{
		StaticMatrix result;
		for(size_t row = 0;row < _Row;++row)
		for(size_t col = 0;col < _Col;++col)
		{
			result.m_Data[row][col] = this->m_Data[row][col] + m.m_Data[row][col];
		}
		return result;
	}
	inline StaticMatrix operator - (const StaticMatrix& m)const
	{
		StaticMatrix result;
		for(size_t row = 0;row < _Row;++row)
		for(size_t col = 0;col < _Col;++col)
		{
			result.m_Data[row][col] = this->m_Data[row][col] - m.m_Data[row][col];
		}
		return result;
	}

	inline StaticMatrix operator * (const T& c)const
	{
		StaticMatrix result;
		for(size_t row = 0;row < _Row;++row)
		for(size_t col = 0;col < _Col;++col)
		{
			result.m_Data[row][col] = this->m_Data[row][col] * c;
		}
		return result;
	}
	inline StaticMatrix operator / (const T& c)const
	{
		StaticMatrix result;
		for(size_t row = 0;row < _Row;++row)
		for(size_t col = 0;col < _Col;++col)
		{
			result.m_Data[row][col] = this->m_Data[row][col] / c;
		}
		return result;
	}

	inline StaticMatrix& operator += (const StaticMatrix& m)
	{
		for(size_t row = 0;row < _Row;++row)
		for(size_t col = 0;col < _Col;++col)
		{
			this->m_Data[row][col] += m.m_Data[row][col];
		}
		return *this;
	}
	inline StaticMatrix& operator -= (const StaticMatrix& m)
	{
		for(size_t row = 0;row < _Row;++row)
		for(size_t col = 0;col < _Col;++col)
		{
			this->m_Data[row][col] -= m.m_Data[row][col];
		}
		return *this;
	}
	inline StaticMatrix& operator *= (const T& c)
	{
		for(size_t row = 0;row < _Row;++row)
		for(size_t col = 0;col < _Col;++col)
		{
			this->m_Data[row][col] *= c;
		}
		return *this;
	}
	inline StaticMatrix& operator /= (const T& c)
	{
		for(size_t row = 0;row < _Row;++row)
		for(size_t col = 0;col < _Col;++col)
		{
			this->m_Data[row][col] /= c;
		}
		return *this;
	}

	inline StaticMatrix<T,_Col,_Row,_Equal> operator ~()const
	{
		StaticMatrix<T,_Col,_Row,_Equal> result;
		for(size_t row = 0;row < _Row;++row)
		for(size_t col = 0;col < _Col;++col)
		{
			result.m_Data[col][row] = this->m_Data[row][col];
		}
		return result;
	}

	T Cofact(size_t row,size_t col)const
	{
		StaticMatrix<T,_Row-1,_Col-1,_Equal> cofact_matrix;
		for(size_t r=0;r<_Row;++r)
		for(size_t c=0;c<_Col;++c)
		{
			if ( r == row || c == col ) continue;
			cofact_matrix(r>row?r-1:r,c>col?c-1:c) = m_Data[r][c];
		}
		T result = T();
		for(size_t r=0;r<cofact_matrix.RowNo();++r)
		for(size_t c=0;c<cofact_matrix.ColNo();++c)
		{
			result += cofact_matrix.Cofact(r,c);
		}
		return result * ((row+col)%2 == 0 ? 1 : -1 );
	}

	static const StaticMatrix& I()
	{
		static StaticMatrix matrix_I;

		if ( matrix_I(0,0) != 1 )
		{
			for(unsigned int i=0;i<_Row;++i)
			for(unsigned int j=0;j<_Col;++j)
			{
				if ( i == j ) matrix_I(i,j) = 1;
				else matrix_I(i,j) = 0;
			}
		}

		return matrix_I;
	}


	T m_Data[_Row][_Col];
private:
	_Equal m_Equaler;
};

template<class T,class _Equal >
class StaticMatrix<T,1,1,_Equal >
{
	enum { _Row = 1, _Col = 1};
public:

	StaticMatrix(const _Equal& _equaler=_Equal())
		:m_Equaler(_equaler)
	{
		m_Data[0][0] = T();	
	}

	size_t RowNo () const { return _Row; }
	size_t ColNo () const { return _Col; }

	// Subscript operator
	inline T&		operator () (size_t row, size_t col) 
	{
		assert( row < _Row && col < _Col );
		return m_Data[row][col];
	}
	inline const T& operator () (size_t row, size_t col) const
	{
		assert( row < _Row && col < _Col );
		return m_Data[row][col];
	}
	inline const T& At(size_t row, size_t col)
	{
		assert( row < _Row && col < _Col );
		return m_Data[row][col];
	}

	bool operator==(const StaticMatrix& matrix)const
	{
		return m_Equaler(m_Data[0][0],matrix.m_Data[0][0]);
	}

	bool operator!=(const StaticMatrix& matrix)const
	{
		return !m_Equaler(m_Data[0][0],matrix.m_Data[0][0]);
	};

	StaticMatrix operator + ()const { return *this;}
	StaticMatrix operator - ()const { return *this *= -1}

	StaticMatrix operator + (const StaticMatrix& m)const
	{
		StaticMatrix result;
		result.m_Data[0][0] = this->m_Data[0][0] + m.m_Data[0][0];
		return result;
	}
	StaticMatrix operator - (const StaticMatrix& m)const
	{
		StaticMatrix result;
		result.m_Data[0][0] = this->m_Data[0][0] - m.m_Data[0][0];
		return result;
	}

	StaticMatrix& operator += (const StaticMatrix& m)
	{
		this->m_Data[0][0] += m.m_Data[0][0];
		return *this;
	}
	StaticMatrix& operator -= (const StaticMatrix& m)
	{
		this->m_Data[0][0] -= m.m_Data[0][0];
		return *this;
	}

	StaticMatrix& operator *= (const T& c)
	{
		this->m_Data[0][0] *= c;
		return *this;
	}
	StaticMatrix& operator /= (const T& c)
	{
		this->m_Data[0][0] /= c;
		return *this;
	}
	StaticMatrix operator ~()const
	{
		return *this;
	}

	T Cofact(size_t row,size_t col)const
	{
		return m_Data[0][0];
	}

	static const StaticMatrix& I()
	{
		static StaticMatrix matrix_I;

		if ( matrix_I(0,0) != 1 )
		{
			matrix_I(0,0) = 1;
		}

		return matrix_I;
	}


	T m_Data[_Row][_Col];
private:
	_Equal m_Equaler;
};



template<class T, unsigned int Row,unsigned int ColAndRow, unsigned int Col,class Equaler>
StaticMatrix<T,Row,Col,Equaler> operator * (const StaticMatrix<T,Row,ColAndRow,Equaler>& _left,const StaticMatrix<T,ColAndRow,Col,Equaler>& _right)
{
	StaticMatrix<T,Row,Col,Equaler> result;
	for(size_t row = 0;row < Row;++row)
	for(size_t col = 0;col < Col;++col)
	for(size_t rc = 0;rc < ColAndRow;++rc)
	{
		result(row,col) += _left(row,rc) * _right(rc,col);
	}
	return result;
}



template<class T, unsigned int Row, unsigned int Col, class Equaler>
std::ostream& operator<<(std::ostream& stream, const StaticMatrix<T, Row, Col, Equaler>& matrix)
{
	for(size_t i=0;i<matrix.RowNo();++i)
	{
		for(size_t j=0;j<matrix.ColNo();++j)
		{
			stream<<matrix(i,j)<<" ";
		}
		stream<<std::endl;
	}

	return stream;
}




#endif